package com.wj1c.push.wechat;

import com.alibaba.fastjson.JSON;
import com.wj1c.push.lock.DistributedLock;
import com.wj1c.push.util.SpringUtils;
import com.wj1c.push.util.StringUtil;
import com.wj1c.push.wechat.request.AccessTokenRequest;
import com.wj1c.push.wechat.response.AccessTokenResponse;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.concurrent.TimeUnit;

/**
 * @author: Mr.Jiao
 * @date: 2018年09月03日
 */
public final class WeChatToken {

    private static WeChatToken instance = null;

    public synchronized static WeChatToken getInstance(){
        if(instance == null){
            instance = new WeChatToken();
        }
        return instance;
    }

    private final String key = "wechat_accesstoken";

    private final String lock_Key = key + "_locked";

    private WeChatToken(){
        redisTemplate = SpringUtils.getBean("redisTemplate");
        lock = new DistributedLock(redisTemplate);
    }

    private RedisTemplate redisTemplate;

    private DistributedLock lock = null;

    private BoundValueOperations<String,String> getValueOperations(){
        BoundValueOperations<String, String> valueOperations = redisTemplate.boundValueOps(key);
        return valueOperations;
    }

    public String getAccessToken(){
        Token token = this.getToken();
        if(token == null || token.isWillExpires()){ //过期时间小于1分钟 或 token 不存在
            token = this.getNewToken();
            if(token == null){
                return null;
            }
        }
        return token.access_token;
    }

    public Token getToken(){
        BoundValueOperations<String,String> valueOperations = this.getValueOperations();
        String tokenJSON = valueOperations.get();
        if(StringUtil.isEmpty(tokenJSON)){
            return null;
        }
        Token token = JSON.parseObject(tokenJSON,Token.class);
        return token;
    }

    private Token getNewToken(){
        boolean locked = lock.lock(lock_Key);
        if(locked) {
            Token token = this.getToken();
            if(token == null || token.isWillExpires(this.getCurrTime())) {
                try {
                    token = this.refreshToken();
                    if (token != null) {
                        BoundValueOperations<String, String> valueOperations = this.getValueOperations();
                        valueOperations.set(JSON.toJSONString(token), 2, TimeUnit.HOURS);
                    }
                } finally {
                    lock.release_lock(lock_Key);
                }
            }
            return token;
        }else{
            long expires_time = this.getCurrTime() + 10;
            Token token = null;
            while (this.getCurrTime() < expires_time){
                token = this.getToken();
                if(token != null && !token.isWillExpires()){
                    break;
                }
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return token;
        }
    }

    private Token refreshToken(){
        long time = this.getCurrTime();
        AccessTokenRequest request = new AccessTokenRequest();
        AccessTokenResponse response = WeChatRequest.exec(request);
        Token token = null;
        assert response.isOk() : String.format("accesstoken can not refresh:%s",response.getErrmsg());
        if(response.isOk()) {
            token = new Token();
            token.expires_in = response.getExpires_in();
            token.access_token = response.getAccess_token();
            token.expires_time = time + response.getExpires_in();
        }
        return token;
    }

    private long getCurrTime(){
        return System.currentTimeMillis() / 1000;
    }

    public static String getAccesstoken(){
        return WeChatToken.getInstance().getAccessToken();
    }
}
